#include <QString>
#include <QtTest>
#include <qtextstream.h>
#include <qlist.h>

#include "showfileparser.h"
#include "lightpresetdata.h"
#include "sequenceitem.h"

#include "TestShowFiles.h"

#include "showfileparser_test.h"

/** Relative path where example files are located.
  */
#define  BASE_TEST_PATH  "/ShowFileParser/test"
#define  FINAL_TEST_PATH  "/ShowFileParser/test/BigFinalTest.mts"
#define  FAKE_DIR  "/Fake/Dir/"

/** number of channels for this test */
#define CHANNELS_FOR_TEST  10

ShowFileParser_Test::ShowFileParser_Test()
{
}

/**
 * Test helper: instantiate the parser and parse text read from \p stream
 * @param stream - text stream
 * @return the instance of ShowFileParser
 */
ShowFileParser *ShowFileParser_Test::runParserForStream(char *stream)
{
   QTextStream input( stream );
   QDir basePath( QString(PROJECT_PATH) + BASE_TEST_PATH);
   ShowFileParser *parser = new ShowFileParser( input, basePath, CHANNELS_FOR_TEST, this);

   parser->parse();

   return parser;
}

void ShowFileParser_Test::initTestCase()
{
}

void ShowFileParser_Test::cleanupTestCase()
{
}

/** executed before every test case */
void ShowFileParser_Test::init()
{
}

/** executed after every test case */
void ShowFileParser_Test::cleanup()
{
}

void ShowFileParser_Test::testCase_init()
{
   QTextStream input( EMPTY_FILE);
   ShowFileParser *parser = new ShowFileParser( input, QDir(FAKE_DIR), CHANNELS_FOR_TEST, this );

   parser->parse();
   QCOMPARE( parser->errorLog().length(), 0 );
}

void ShowFileParser_Test::testCase_invalidInput()
{
   bool exceptionRaised = false;
   ShowFileParser *parser = NULL;
   QString exceptionMessage;
   QTextStream nullStream;

   try
   {
      parser = new ShowFileParser( nullStream, QDir(FAKE_DIR), CHANNELS_FOR_TEST, this);
   }
   catch(QString &msg)
   {
      exceptionMessage = msg;
      exceptionRaised = true;
   }

   QVERIFY( NULL == parser );
   QVERIFY( exceptionRaised == true );
   QCOMPARE( exceptionMessage, QString("Assert fail: input.device() != NULL") );
}

/** a valid show file, but with no tracks, lighset or script */
void ShowFileParser_Test::testCase_emptyShow()
{
   QTextStream input( EMPTY_SHOW );
   ShowFileParser *parser = new ShowFileParser( input, QDir(FAKE_DIR), CHANNELS_FOR_TEST, this);

   try
   {
      parser->parse();
   }
   catch( QString &msg )
   {
      QFAIL( (QString("unexpected exception: ") + msg).toLatin1() );
   }

   QCOMPARE( parser->errorLog().length(), 0 );
}

/** one valid track */
void ShowFileParser_Test::testCase_OneTrack()
{
   QDir basePath( QString(PROJECT_PATH) + BASE_TEST_PATH);
   ShowFileParser *parser = runParserForStream( TRACK_1_LIGHT_1 );

   QCOMPARE( parser->errorLog().length(), 0 );
   QCOMPARE( parser->trackList().length(), 1 );

   QCOMPARE( parser->trackList().at(0), basePath.absolutePath() + "/" + PLAYLIST_LINE_1 );
}

/** one valid lightset */
void ShowFileParser_Test::testCase_OneLight()
{
   ShowFileParser *parser = runParserForStream( TRACK_1_LIGHT_1 );

   QCOMPARE( parser->errorLog().length(), 0);
   QCOMPARE( parser->lightsetList().count(), 1 );
   QCOMPARE( parser->lightsetList().at(0)->name(), QString::fromLatin1("SETTING 1") );
}

void ShowFileParser_Test::testCase_OneSequenceItem()
{
   ShowFileParser *parser = runParserForStream( TRACK_1_SEQUENCE_1 );

   QCOMPARE( parser->errorLog().length(), 0);
   QCOMPARE( parser->trackList().count(), 1);

   QCOMPARE( parser->sequenceItemList().count(), 2);

   QCOMPARE( parser->sequenceItemList().at(0)->type(), SequenceItem::TRACK);
   QCOMPARE( parser->sequenceItemList().at(0)->label(), QString::fromLatin1("1_Traccia_1.mp3"));
   QCOMPARE( parser->sequenceItemList().at(0)->delayMs(), 100);
   QCOMPARE( parser->sequenceItemList().at(0)->indexOfNext(), SequenceItem::NEXT_IS_UNDEFINED);

   QCOMPARE( parser->sequenceItemList().at(1)->type(), SequenceItem::LIGHT);
   QCOMPARE( parser->sequenceItemList().at(1)->label(), QString::fromLatin1("stanza"));
   QCOMPARE( parser->sequenceItemList().at(1)->delayMs(), -1);
   QCOMPARE( parser->sequenceItemList().at(1)->indexOfNext(), 0);
}

/** in this file, lightset list is before track list,
 *  differently from what should be.
 */
void ShowFileParser_Test::testCase_InverseTagOrder()
{
   ShowFileParser *parser = runParserForStream( TRACK_1_LIGHT_1 );

   QCOMPARE( parser->errorLog().length(), 0);
   QCOMPARE( parser->trackList().count(), 1 );
   QCOMPARE( parser->lightsetList().count(), 1 );
}

/** this file has playlist after html tag. Tracks are not loaded */
void ShowFileParser_Test::testCase_tagAfterHtml()
{
   ShowFileParser *parser = runParserForStream( TRACKS_AFTER_HTML );

   QCOMPARE( parser->errorLog().length(), 0);
   QCOMPARE( parser->trackList().count(), 0 );
   QCOMPARE( parser->lightsetList().count(), 1 );
}

/** it is allowed to have more light values or less than
 *  the actual value of channels
 */
void ShowFileParser_Test::testCase_lightsetFewValues()
{
   ShowFileParser *parser = runParserForStream( LIGHT_NUMBER_OF_VALUES );

   QCOMPARE( parser->errorLog().length(), 0);
   QCOMPARE( parser->lightsetList().count(), 2 );
}

void ShowFileParser_Test::testCase_TrackEndMissing()
{
   ShowFileParser *parser = runParserForStream( NO_TRACK_END );

   /* 1 error for missing "<PLAYLIST-->" and 5 lines considered bad files */
   QCOMPARE( parser->errorLog().length(), 6);
   QCOMPARE( parser->errorLog().at(5), QString("End of features but <PLAYLIST--> not found"));
}

/** lightset line has no setting name */
void ShowFileParser_Test::testCase_lightsetErrorName()
{
   ShowFileParser *parser = runParserForStream( LIGHTSET_ERROR_NO_NAME );

   QCOMPARE( parser->errorLog().length(), 1);
   QVERIFY( parser->errorLog().at(0).contains( QString("Name or numeric values missing") ));
   QVERIFY( parser->errorLog().at(0).contains( QString(LIGHTSET_MISSING_NAME) ));
}

/** if lightset has not enough lights, missing must be filled with 0 */
void ShowFileParser_Test::testCase_notEnoughLights()
{
   QString content(LIGHTSET_START_TAG"\n"\
                   LIGHTSET_LINE_SHORT"\n"\
                   LIGHTSET_END_TAG);

   ShowFileParser *parser = runParserForStream( content.toLatin1().data() );
   QCOMPARE( parser->lightsetList().at(0)->levelList().size(), CHANNELS_FOR_TEST);
}


void ShowFileParser_Test::testCase_twoErrors()
{
   ShowFileParser *parser = runParserForStream( LIGHTSET_TWO_ERRORS );

   QCOMPARE( parser->errorLog().length(), 2);
   QVERIFY( parser->errorLog().at(0).contains( QString("Name or numeric values missing") ));
   QVERIFY( parser->errorLog().at(1).contains( QString("Name or numeric values missing") ));
}

/** lighset line has only one number after name */
void ShowFileParser_Test::testCase_lightsetHasNoValues()
{
   ShowFileParser *parser = runParserForStream( LIGHTSET_ERROR_NO_VALUE );

   QCOMPARE( parser->errorLog().length(), 1);
   QVERIFY( parser->errorLog().at(0).contains( QString("Not enough numbers") ));
   QVERIFY( parser->errorLog().at(0).contains( QString(LIGHTSET_NO_VALUES) ));
}

void ShowFileParser_Test::testCase_lightsetHasBadValue()
{
   ShowFileParser *parser = runParserForStream( LIGHTSET_ERROR_BAD_VALUE );

   QCOMPARE( parser->errorLog().length(), 1);
   QVERIFY( parser->errorLog().at(0).contains( QString("There are bad numerics") ));
   QVERIFY( parser->errorLog().at(0).contains( QString(LIGHTSET_BAD_VALUE) ));
}

void ShowFileParser_Test::testCase_unsupportedMediaExtention()
{
   ShowFileParser *parser = runParserForStream( LIGHTSET_ERROR_UNSUPPORTED_MEDIA_EXT );

   QCOMPARE( parser->errorLog().length(), 1);
   QVERIFY( parser->errorLog().at(0).contains( QString("File not found or unsupported") ));
   QVERIFY( parser->errorLog().at(0).contains( QString(PLAYLIST_WRONG_EXT) ));
}

void ShowFileParser_Test::testCase_wrongSequenceLine()
{
   ShowFileParser *parser = runParserForStream( WRONG_SEQUENCE );

   QCOMPARE( parser->errorLog().length(), 1);
   QVERIFY( parser->errorLog().at(0).contains( QString("Not a valid sequence line") ));
}

#include <QFile>

/** a simple, but not trivial, exaple file
 *  that tests the positive path
 */
void ShowFileParser_Test::testCase_bigFinalTest()
{
   QFile showFile( QString(PROJECT_PATH) + FINAL_TEST_PATH );
   QDir basePath( QString(PROJECT_PATH) + BASE_TEST_PATH );
   showFile.open( QIODevice::ReadOnly );

   if (!showFile.isOpen())
   {
      QFAIL( "can not open source file" );
   }

   QTextStream input( &showFile );
   ShowFileParser *parser = new ShowFileParser( input, basePath, CHANNELS_FOR_TEST, this);

   try
   {
      parser->parse();
   }
   catch( QString &msg )
   {
      QFAIL( (QString("unexpected exception: ") + msg).toLatin1() );
   }

   showFile.close();

   QCOMPARE( parser->errorLog().length(), 1);  /* one media file does not exist */
   QCOMPARE( parser->trackList().count(), 3 );
   QCOMPARE( parser->lightsetList().count(), 3 );
}
